/**
 *
 */
package wsepForum.network.client;

import java.io.*;
import java.net.Socket;
import java.util.Vector;

import javax.swing.JOptionPane;

import wsepForum.logging.ForumLogger;
import wsepForum.logging.ForumLogger.LoggerCategory;
import wsepForum.network.common.GUIAnswer;
import wsepForum.network.server.*;

/**
 * @author Oren Ben-Haim & Amir Harel
 *
 */
public class SimpleClient extends Thread {
	/*****************************
	 * MEMBERS                   *
	 *****************************/
	/**
	 * The server socket
	 */
	private Socket socket;
	/**
	 * if true the connection should disconnect
	 */
	private boolean disconnectFlag;
	/**
	 *
	 */
	private String serverAddress;
	/**
	 *
	 */
	private int serverPort;
	/**
	 * size of the buffer used to read from the line
	 */
	public static final int BUFFERSIZE = 35000;
	/** in from the line */
	private BufferedReader in;
	/** out from the line */
	private PrintWriter out = null;
	/**
	 * the unit that breaks lines apart
	 */
	private GUITokenizer<GUIAnswer> tokenizer;
	/**
	 * The UI controller
	 */
	private GUIController controller;




	/*****************************
	 * CONSTRUCTOR               *
	 *****************************/
	/**
	 * @param serverAddr
	 * @param serverPort
	 * @param uiController The UI controller
	 */
	public SimpleClient(String serverAddr, int serverPort, GUIController uiController) {
		this.serverAddress = serverAddr;
		this.serverPort = serverPort;
		this.socket=null;
		this.disconnectFlag = false;
		this.controller = uiController;
		this.tokenizer = new GUITokenizer<GUIAnswer>();
	}
	/*****************************
	 * METHODS                   *
	 *****************************/
	/**
	 * Main actor of the connection
	 */
	public void run()
	{
		ForumLogger logger = ForumLogger.getInstance();
		try
		{
			this.initialize();
		}
		catch (IOException e)
		{
			JOptionPane.showMessageDialog(null, "Error in initializing I/O with server!!\nplease try again later.");
			logger.error("Error in initializing I/O with server!!", LoggerCategory.NETWORK);
			System.exit(0);
		}

		logger.info("Opened connection with " + this.socket.getRemoteSocketAddress().toString(),LoggerCategory.NETWORK);
		try
		{
			this.process();
		}
		catch (IOException e)
		{}
		logger.info("Closing connection with " + this.socket.getRemoteSocketAddress().toString(),LoggerCategory.NETWORK);
		if (!this.disconnectFlag) this.close();
	}

	/**
	 * main loop of the connection, process incoming data
	 * @throws IOException .
	 */
	private void process() throws IOException
	{
		Vector<String> respond;
		// read data from socket
		final char[] buffer = new char[ConsoleConnectionHandler.BUFFERSIZE];
		int charsRead = -1;
		while (((charsRead = this.in.read(buffer)) != -1) && !this.disconnectFlag)
		{
			if (charsRead <= 0)
				continue;

			// parse the bytes into a string
			String msg =  new String(buffer, 0, charsRead);
			respond = new Vector<String>(1);
			// tokenize string for line tokens
			Vector<GUIToken<GUIAnswer>> tokens = this.tokenizer.process(msg);
			if (tokens != null) {
				for (GUIToken<GUIAnswer> guiToken : tokens) {
					respond.add(this.controller.incomingAnswer(guiToken));
				}
			}

			// send data back to the server if needed
			if (respond != null)
			{
				for (final String send : respond)
				{
					if (send == null)
						return;
					// send responds to the line, if null hangup
					if (send.length()>0)
						this.sendString(send);
				}
			}
		}
	}

	/**
	 * send data to the line
	 * @param str string to send
	 * @return true if sent
	 */
	public synchronized boolean sendString(String str)
	{
		try
		{
			// send string to the line
			this.out.write(str+"\0");
			this.out.flush();
			return true;
		}
		catch (Exception e)
		{
			return false;
		}
	}

	/**
	 * prepare the connection to use
	 * @throws IOException .
	 */
	private void initialize() throws IOException
	{
		/**
		 * Initialize I/O
		 */
		synchronized(this) { // TODO other object
			this.socket = new Socket(this.serverAddress,this.serverPort);
		}
		this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
		this.out = new PrintWriter(this.socket.getOutputStream(), true);
	}

	/**
	 * Closes the connection
	 */
	private void close()
	{
		try
		{
			this.disconnectFlag = true;
			if (this.in != null)
				this.in.close();
			if (this.out != null)
				this.out.close();
			this.socket.close();
		}
		catch (IOException e)
		{
			// TODO Loco.LOG.fine("Exception in closing I/O with " + this.name);
		}
	}

	/**
	 * kill the connection by force
	 */
	public void killConnection()
	{
		this.disconnectFlag = true;
		this.close();
	}
}
